package com.puzzlebazar.client.main.view;

import com.google.gwt.event.logical.shared.ResizeEvent;
import com.google.gwt.event.logical.shared.ResizeHandler;
import com.google.gwt.event.shared.HandlerRegistration;
import com.google.gwt.user.client.Window;

import com.google.gwt.user.client.ui.AbsolutePanel;
import com.google.gwt.user.client.ui.HTML;
import com.google.gwt.user.client.ui.HasHorizontalAlignment;
import com.google.gwt.user.client.ui.HasVerticalAlignment;
import com.google.gwt.user.client.ui.HorizontalPanel;
import com.google.gwt.user.client.ui.Hyperlink;
import com.google.gwt.user.client.ui.RootPanel;
import com.google.gwt.user.client.ui.VerticalPanel;
import com.google.gwt.user.client.ui.Widget;
import com.puzzlebazar.client.admin.SiteInfo;
import com.puzzlebazar.client.admin.SiteModel;
import com.puzzlebazar.client.user.UserInfo;
import com.puzzlebazar.client.user.UserModel;
import com.puzzlebazar.client.util.Observable;
import com.puzzlebazar.client.util.Observer;
import com.puzzlebazar.client.util.Vec2i;

// +----------------------------------------------------------------------------+
// | topBar  +----------------------------------------------------------------+ |
// |         | loginBar                                                       | |
// |         +----------------------------------------------------------------+ |
// +----------------------------------------------------------------------------+
// +----------------------------------------------------------------------------+
// | topSpacer                                                                  |
// +----------------------------------------------------------------------------+
// +----------------------------------------------------------------------------+
// | mainArea                                                                   |
// | +---------+ +---------------+ +-------------------------------+ +--------+ |
// | |left     | |leftPanel      | |rightPanel                     | |right   | |
// | |  Spacer | | linkGroupsView| | +---------------------------+ | |  Spacer| |
// | |         | |               | | | mainPanelDecoTop          | | |        | |
// | |         | |               | | +---------------------------+ | |        | |
// | |         | |               | | +---------------------------+ | |        | |
// | |         | |               | | | mainPanel                 | | |        | |
// | |         | |               | | |                           | | |        | |
// | |         | |               | | |                           | | |        | |
// | |         | |               | | |                           | | |        | |
// | |         | |               | | |                           | | |        | |
// | |         | |               | | |                           | | |        | |
// | |         | |               | | +---------------------------+ | |        | |
// | |         | |               | | +---------------------------+ | |        | |
// | |         | |               | | | mainPanelDecoBot          | | |        | |
// | |         | |               | | +---------------------------+ | |        | |
// | +---------+ +---------------+ +-------------------------------+ +--------+ |
// +----------------------------------------------------------------------------+


public class MainView implements Observer, UpdateTokenCallback {

	public enum VIEW_TYPES {
		standard, noLeftBar, bare 
	}

	private static final int TOPSPACER_HEIGHT = 8;
	private static final int BOTSPACER_HEIGHT = 8;
	private static final int LEFTBAR_WIDTH = 120;
	private static final int LEFTSPACER_WIDTH = 8;
	private static final int RIGHTSPACER_WIDTH = 8;

	private static final int TOPDECO_HEIGHT = 30;
	private static final int BOTDECO_HEIGHT = 26;
	private static final int LEFTDECO_WIDTH = 10;
	private static final int RIGHTDECO_WIDTH = 10;
	
	private final UserModel userModel;
	private final SiteModel settingsModel;

	private final MyResizeHandler resizeHandler = new MyResizeHandler();

	// ---- TOP BAR ----
	private final VerticalPanel topBar = new VerticalPanel(); 	
	private final HorizontalPanel loginBar = new HorizontalPanel();

	// ---- TOP SPACER ----
	private final AbsolutePanel topSpacer = new AbsolutePanel();
	
	// ---- MAIN AREA ----
	private final HorizontalPanel mainArea = new HorizontalPanel();
	
	private final HTML leftSpacer = new HTML("&nbsp;");	

	private final VerticalPanel leftPanel = new VerticalPanel();
	private final LinkGroupsView linkGroupsView;
	
	private final VerticalPanel rightPanel = new VerticalPanel();
	private final HorizontalPanel mainPanelDecoTop = new HorizontalPanel();
	private final HTML mainPanelTitle;
	private final VerticalPanel mainPanel = new VerticalPanel();
	private final HorizontalPanel mainPanelDecoBot = new HorizontalPanel();
	
	private final HTML rightSpacer = new HTML("&nbsp;");	

	private MainTokenFactory tokenFactory = null;

	private boolean loginEnabled = true;

	private ScreenSizeListener resizableMainPanel = null;
	private HandlerRegistration resizeHandlerRegistration = null;

	private Object currentView = VIEW_TYPES.standard;
	
	
	/**
	 * Creates a new standard main view
	 * @param settingsModel  Settings model to view
	 * @param userModel  User model to view
	 * 
	 * @throws Exception
	 */
	public MainView( SiteModel settingsModel, UserModel userModel ) throws Exception {
		super();
		this.userModel = userModel;
		linkGroupsView = new LinkGroupsView(userModel);
		this.settingsModel = settingsModel;

		// ---- ROOT PANEL ----
		topBar.setWidth( "100%" );
		topBar.setHorizontalAlignment(HorizontalPanel.ALIGN_RIGHT);
		topBar.setStylePrimaryName("topBar");

		topSpacer.setHeight( TOPSPACER_HEIGHT + "px" );
		
		mainArea.setWidth( "100%" );
		
		RootPanel.get().add( topBar );
		RootPanel.get().add( topSpacer );
		RootPanel.get().add( mainArea );

		// ---- TOP BAR ----				
		loginBar.setHorizontalAlignment(HorizontalPanel.ALIGN_RIGHT);
		topBar.add( loginBar );

		// ---- MAIN AREA ----
		leftSpacer.setWidth( LEFTSPACER_WIDTH + "px" );

		leftPanel.setWidth( LEFTBAR_WIDTH + "px" );

		rightPanel.setWidth( "100%" );

		rightSpacer.setWidth( RIGHTSPACER_WIDTH + "px" );
		
		mainArea.add( leftSpacer );
		mainArea.setCellWidth(leftSpacer, LEFTSPACER_WIDTH + "px");
		mainArea.add( leftPanel );
		mainArea.setCellWidth(leftPanel, LEFTBAR_WIDTH + "px");
		mainArea.add( rightPanel );
		mainArea.setCellWidth(rightPanel, "100%" );
		mainArea.add( rightSpacer );
		mainArea.setCellWidth(rightSpacer, RIGHTSPACER_WIDTH + "px");

		// ---- LEFT PANEL ----
		linkGroupsView.setWidth( "100%" );
		leftPanel.add( linkGroupsView );
		
		// ---- RIGHT PANEL ----
		mainPanelDecoTop.setWidth( "100%" );
		mainPanelDecoTop.setHeight( TOPDECO_HEIGHT + "px" );

		mainPanel.setWidth( "100%" );
		mainPanel.setStylePrimaryName( "decoratedMainPanel" );
		mainPanel.setHorizontalAlignment(VerticalPanel.ALIGN_CENTER);
		
		mainPanelDecoBot.setWidth( "100%" );
		mainPanelDecoBot.setHeight( BOTDECO_HEIGHT + "px" );

		rightPanel.add( mainPanelDecoTop );
		rightPanel.setCellHeight(mainPanelDecoTop, TOPDECO_HEIGHT + "px");
		rightPanel.add( mainPanel );
		rightPanel.add( mainPanelDecoBot );
		rightPanel.setCellHeight(mainPanelDecoBot, BOTDECO_HEIGHT + "px");
		
		// ---- MAIN PANEL DECO TOP ----
		HTML topLeftDeco = new HTML("&nbsp;");
		topLeftDeco.setStylePrimaryName( "topLeftDeco" );
		topLeftDeco.setWidth( LEFTDECO_WIDTH + "px" );
		topLeftDeco.setHeight( TOPDECO_HEIGHT + "px" );
		
		mainPanelTitle = new HTML("&nbsp;");
		mainPanelTitle.setStylePrimaryName( "mainPanelTitle" );
		mainPanelTitle.setHeight( TOPDECO_HEIGHT + "px" );
		mainPanelTitle.setHorizontalAlignment( HasHorizontalAlignment.ALIGN_LEFT );

		HTML topRightDeco = new HTML("&nbsp;");
		topRightDeco.setStylePrimaryName( "topRightDeco" );
		topRightDeco.setWidth( RIGHTDECO_WIDTH + "px" );
		topRightDeco.setHeight( TOPDECO_HEIGHT + "px" );
		
		mainPanelDecoTop.add( topLeftDeco );
		mainPanelDecoTop.setCellWidth(topLeftDeco, LEFTDECO_WIDTH + "px");
		mainPanelDecoTop.setCellHeight(topLeftDeco, TOPDECO_HEIGHT + "px");
		mainPanelDecoTop.add( mainPanelTitle );
		mainPanelDecoTop.setCellHeight(mainPanelTitle, TOPDECO_HEIGHT + "px");
		mainPanelDecoTop.setCellVerticalAlignment( mainPanelTitle, HasVerticalAlignment.ALIGN_MIDDLE );
		mainPanelDecoTop.add( topRightDeco );
		mainPanelDecoTop.setCellWidth(topRightDeco, RIGHTDECO_WIDTH + "px");
		mainPanelDecoTop.setCellHeight(topRightDeco, TOPDECO_HEIGHT + "px");
		
		// ---- MAIN PANEL DECO BOT ----
		HTML botLeftDeco = new HTML("&nbsp;");
		botLeftDeco.setStylePrimaryName( "botLeftDeco" );
		botLeftDeco.setWidth( LEFTDECO_WIDTH + "px" );
		botLeftDeco.setHeight( BOTDECO_HEIGHT + "px" );
		
		HTML botDeco = new HTML("&nbsp;");
		botDeco.setStylePrimaryName( "botDeco" );
		botDeco.setHeight( BOTDECO_HEIGHT + "px" );

		HTML botRightDeco = new HTML("&nbsp;");
		botRightDeco.setStylePrimaryName( "botRightDeco" );
		botRightDeco.setWidth( RIGHTDECO_WIDTH + "px" );
		botRightDeco.setHeight( BOTDECO_HEIGHT + "px" );
		
		mainPanelDecoBot.add( botLeftDeco );
		mainPanelDecoBot.setCellWidth(botLeftDeco, LEFTDECO_WIDTH + "px");
		mainPanelDecoBot.setCellHeight(botLeftDeco, BOTDECO_HEIGHT + "px");
		mainPanelDecoBot.add( botDeco );
		mainPanelDecoBot.setCellHeight(botDeco, BOTDECO_HEIGHT + "px");
		mainPanelDecoBot.add( botRightDeco );
		mainPanelDecoBot.setCellWidth(botRightDeco, RIGHTDECO_WIDTH + "px");
		mainPanelDecoBot.setCellHeight(botRightDeco, BOTDECO_HEIGHT + "px");
		
		
		// Initialize
		
		createLoginBar();		

		userModel.addObserver( this );
		settingsModel.addObserver( this );
	}

	
	@Override
	public void update(Observable observable, Object arg) {
		updateTokens();
	}

	@Override
	public void updateTokens() {
		createLoginBar();
		linkGroupsView.updateTokens();
		
		Widget mainWidget = null;
		
		if( mainPanel.getWidgetCount() > 0 )
			mainWidget = mainPanel.getWidget(0);
		
		if( mainWidget != null && mainWidget instanceof UpdateTokenCallback )
			((UpdateTokenCallback)mainWidget).updateTokens();
				
	}

	/**
	 * Creates the links and labels to display in the login bar. The passed
	 * panel will be cleared before links are added to it.
	 */
	private void createLoginBar() {
		
		loginBar.clear();
		UserInfo userInfo = userModel.getUserInfo();
		SiteInfo siteInfo = settingsModel.getSiteInfo();
		
		if( userInfo == null ) {
			loginBar.add( new HTML( "Not logged in.&nbsp;&nbsp;|&nbsp;&nbsp;" ));
	        
			if( loginEnabled ) {
				loginBar.add( new HTML( "<a href=\"" +
						siteInfo.getLoginURL() +"\">" +
						"login or register</a>" ));
			}			
		}
		else {
			loginBar.add( new HTML( "<b>"+userInfo.getNickname()+"</b>&nbsp;&nbsp;|&nbsp;&nbsp;" ));
			loginBar.add( new HTML( "<a href=\"" +
					siteInfo.getLogoutURL() +"\">" +
			"logout</a>" ) );
			loginBar.add( new HTML( "&nbsp;&nbsp;|&nbsp;&nbsp;" ));
			loginBar.add( new Hyperlink( "settings", getSettingsToken() ));			
		}
		loginBar.add( new HTML( "&nbsp;&nbsp;" ));
	}

	/**
	 * @return The toekn to access the settings of the user
	 */
	private String getSettingsToken() {
		if( tokenFactory == null ) return "";
		return tokenFactory.createSettingsToken();
	}
	
	/**
	 * Sets a new token factory
	 * 
	 * @param tokenFactory The new token factory to use
	 */
	public void setTokenFactory(MainTokenFactory tokenFactory) {
		this.tokenFactory = tokenFactory;
		updateTokens();
	}

	/**
	 * Sets the main panel title
	 * 
	 * @param title Sets the new title, null to remove title
	 */
	public void setMainPanelTitle( String title ) {
		if( title == null )
			mainPanelTitle.setHTML( "&nbsp;" );
		else
			mainPanelTitle.setHTML( title );
	}
	
	
	/**
	 * Determines if login should be enabled
	 * 
	 * @param enabled True to enable it, false to disable it
	 */
	public void enableLogin( boolean enabled ) {
		if( loginEnabled == enabled ) return;
		loginEnabled  = enabled;
		createLoginBar();
	}

	/**
	 * Determines if registration should be enabled
	 * 
	 * @param enabled True to enable it, false to disable it
	 */
	public void enableRegistration(boolean enabled) {
		// TODO Get rid of this
		
		//		if( registrationEnabled == enabled ) return;
//		registrationEnabled  = enabled;
//		createLoginBar();
	}
	
	/**
	 * Sets the standard formatted view
	 */
	public void setStandardView() {
		if( currentView == VIEW_TYPES.standard ) return;
		
		mainArea.insert(leftPanel, 1);
		mainArea.setCellWidth(leftPanel, LEFTBAR_WIDTH + "px");			
		if( currentView == VIEW_TYPES.bare ) {
			rightPanel.insert(mainPanelDecoTop, 0);
			rightPanel.setCellHeight(mainPanelDecoTop, TOPDECO_HEIGHT + "px");
			rightPanel.add(mainPanelDecoBot);
			rightPanel.setCellHeight(mainPanelDecoBot, BOTDECO_HEIGHT + "px");
			mainPanel.setStylePrimaryName( "decoratedMainPanel" );
			mainPanelDecoTop.setVisible( true );			
		}
		
		currentView = VIEW_TYPES.standard;
		
		refreshView();
	}
	

	/**
	 * Sets the standard formatted view
	 */
	public void setNoLeftBarView() {
		if( currentView == VIEW_TYPES.noLeftBar ) return;
		
		if( currentView == VIEW_TYPES.standard ) {
			leftPanel.removeFromParent();			
		}
		if( currentView == VIEW_TYPES.bare ) {
			rightPanel.insert(mainPanelDecoTop, 0);
			rightPanel.setCellHeight(mainPanelDecoTop, TOPDECO_HEIGHT + "px");
			rightPanel.add(mainPanelDecoBot);
			rightPanel.setCellHeight(mainPanelDecoBot, BOTDECO_HEIGHT + "px");
			mainPanel.setStylePrimaryName( "decoratedMainPanel" );
			mainPanelDecoTop.setVisible( true );						
		}

		currentView = VIEW_TYPES.noLeftBar;
		
		refreshView();
	}
		

	/**
	 * Sets the standard formatted view
	 */
	public void setBareView() {
		if( currentView == VIEW_TYPES.bare ) return;
		
		if( currentView == VIEW_TYPES.standard ) {
			leftPanel.removeFromParent();
		}
		mainPanelDecoTop.removeFromParent();
		mainPanelDecoBot.removeFromParent();
		mainPanel.setStylePrimaryName( "undecoratedMainPanel" );
		mainPanelDecoTop.setVisible( false );

		currentView = VIEW_TYPES.bare;
		
		refreshView();
	}
	
	/**
	 * Sets the main widget of the view, the one that will be displayed in the large
	 * right panel
	 * 
	 * @param widget The widget to use as a main widget
	 */
	public void setMainWidget(Widget widget) {
		if( resizableMainPanel != null ) {
			resizeHandlerRegistration.removeHandler();
			resizableMainPanel = null;
		}
		mainPanel.clear();
		mainPanel.add(widget);
		if( widget instanceof ScreenSizeListener ) {
			resizableMainPanel = (ScreenSizeListener)widget;
			resizeHandlerRegistration  = Window.addResizeHandler( resizeHandler );
		}
		refreshView();
	}			
	
	/**
	 * Access the size, in pixel, of the main widget area
	 * 
	 * @return The main widget area size, in pixels
	 */
	public Vec2i getMainWidgetSize() {
		Vec2i result = new Vec2i(
				Window.getClientWidth(),
				Window.getClientHeight() );
		
		result.y -= topBar.getOffsetHeight();
		result.y -= topSpacer.getOffsetHeight();
		result.x -= leftSpacer.getOffsetWidth();
		result.y -= BOTSPACER_HEIGHT;
		if( currentView != VIEW_TYPES.noLeftBar )
			result.x -= leftPanel.getOffsetWidth();			
		if( currentView != VIEW_TYPES.bare ) {
			result.x -= rightSpacer.getOffsetWidth();
			result.y -= mainPanelDecoTop.getOffsetHeight();
			result.y -= mainPanelDecoBot.getOffsetHeight();
		}
		
		return result;
	}
	
	/**
	 * Method called whenever the screen size has changed of when children change their organization
	 * in a way that may impact the size of the various widgets
	 */
	public void refreshView() {
		if( resizableMainPanel != null )
			resizableMainPanel.screenSizeChanged( getMainWidgetSize() );
	}

	public class MyResizeHandler implements ResizeHandler {
		@Override
		public void onResize(ResizeEvent event) {
			refreshView();
		}
	}


	public interface MainTokenFactory {
		
		/**
		 * Creates an history token that can be used to access the user profile
		 * 
		 * @return The history token
		 */
		String createSettingsToken();

	}


	public interface MainCallback {

		/**
		 * Called whenever the logout button is clicked
		 */
		public void logout();
	}	

	@Override
	public void release() {
		userModel.deleteObserver(this);
		settingsModel.deleteObserver(this);
	}
	
}
